/*
 * anima/cmdline.cc - Command line parser implementation.
 * Copyright (C) 2007  Mikhail Vorozhtsov
 * See the LICENSE section of the README file for details.
 */

/* $Id$ */

#include HEADER (anima,cmdline.hh)
#include HEADER (anima,console.hh)
#include HEADER (utils,memory.hh)

START_NAMESPACE (anima)

bool
cmdline::option_t::set (const char *str, u64_t n) const
{
  if (type == TYPE_STRING)
    {
      if (n > value.length)
        return false;

      utils::cpz (str, value.string, n);
    }
  else if (type == TYPE_ENUM)
    {
      bool found_p = false;

      for (u64_t i = 0; restrictions.items[i]; ++i)
        if (utils::cmpz (str, n, restrictions.items[i]) == 0)
          {
            *value.item = restrictions.items[i];
            found_p = true;
            break;
          }

      if (!found_p)
        return false;
    }
  else
    return false;

  if (defined_p)
    *defined_p = true;

  return true;
}

bool
cmdline::option_t::set (u64_t x) const
{
  if (type != TYPE_INTEGER)
    return false;

  *value.integer = x;

  if (defined_p)
    *defined_p = true;

  return true;
}

static bool
process (const cmdline::option_t * const options[],
         const char *name, u64_t name_len,
         const char *value, u64_t value_len, bool dry_run)
{
  for (u64_t i = 0; ; ++i)
    {
      const cmdline::option_t *option = options[i];

      if (!option)
        break;

      if (utils::cmpz (name, name_len, option->name))
        continue;

      if (option->type == cmdline::option_t::TYPE_STRING)
        {
          if (!option->set (value, value_len) && dry_run)
            console::print (
              "Value for option `%S' is too long.", name, name_len);
        }
      else if (option->type == cmdline::option_t::TYPE_INTEGER)
        {
        }
      else if (option->type == cmdline::option_t::TYPE_ENUM)
        {
          if (!option->set (value, value_len) && dry_run)
            console::print (
              "Unexpected value for option `%S'.", name, name_len);
        }

      return true;
    }

  return false;
}

bool
cmdline::parse (const char *str, bool dry_run)
{
  bool success_p = true;

  while (1)
    {
      const char *name, *value, *junk = NULL;
      u64_t name_len, value_len;
      char c = *str;

      while (c != 0 && !(c >= 'a' && c <= 'z'))
        {
          if (c != ' ' && c != '\t' && dry_run && junk == NULL)
            junk = str;
          c = *++str;
        }

      if (dry_run && junk)
        {
          console::print ("%u\n", str - junk);
          console::error (
            "Junk characters in command line: %S.", junk, str - junk);
          success_p = false;
        }

      if (c == 0)
        break;

      name = str; 
      c = *++str;

      while (c != 0 && ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')
                        || c == '_' || c == ':'))
        c = *++str;

      if (c == 0)
        {
          if (dry_run)
            {
              console::error ("Unassigned option `%s'.", name);
              success_p = false;
            }
          break;
        }

      if (c != '=')
        {
          if (dry_run)
            {
              console::error ("Unassigned option `%S'.", name, str - name);
              success_p = false;
            }
          continue;
        }

      name_len = str - name;
      c = *++str;
      value = str;

      while (c != 0 && c != ' ' && c != '\t')
        c = *++str;

      value_len = str - value;

      if (!process (arch_options, name, name_len, value, value_len, dry_run)
          && !process (
                platform_options, name, name_len, value, value_len, dry_run)
          && !process (
                machine_options, name, name_len, value, value_len, dry_run)
          && dry_run)
        {
          console::error ("Unknown option `%S'.", name, name_len);
          success_p = false;
        }
    }

  return success_p;
}

END_NAMESPACE

